﻿#ifndef CAMERAPROXY_H
#define CAMERAPROXY_H
#include "AbstractCamera.hpp"
#include "Spinnaker.h"
#include "SpinGenApi/SpinnakerGenApi.h"
#include <queue>
using namespace Spinnaker;
using namespace Spinnaker::GenApi;
using namespace Spinnaker::GenICam;
using namespace std;
queue<ImagePtr> rgbqueue;
queue<ImagePtr> spequeue;
class ImageEventHandlerImpl : public ImageEventHandler
{
public:
	ImageEventHandlerImpl(string deviceSerial)
	{
		// The device serial number is stored so the grab information can be updated
		// on each image event.
		m_deviceSerialNumber = deviceSerial;
	}

	~ImageEventHandlerImpl()
	{
	}

	//
	// *** NOTES ***
	// The implementation for OnImageEvent is defined later as it requires the implementation
	// of the GrabInfo struct below. GrabInfo relies on the implementation of the
	// ImageEventHandlerImpl constructor so it is defined first.
	//
	void OnImageEvent(ImagePtr image) {
		if (image->IsIncomplete())
		{
			cout << "Image incomplete with image status " << image->GetImageStatus() << "..." << endl
				<< endl;
			return;
		}
		ImagePtr convertedImage;
		if (m_deviceSerialNumber == "20137880") {
			convertedImage = image->Convert(PixelFormat_RGB8, HQ_LINEAR);
			rgbqueue.push(convertedImage);
		}
		else {
			convertedImage = image->Convert(PixelFormat_Mono8, HQ_LINEAR);
			spequeue.push(convertedImage);
		}
			
		ostringstream filename;
		int cur, channle, bit;
		if (nums >=0)
			filename << m_deviceSerialNumber << "-" << to_string(nums) << ".png";
		else
		{
			int tmp = nums - 1;
			cur = tmp / 24;
			tmp %= 24;
			channle = tmp / 8;
			//if (channle == 0) {
			//	channle
			//}
			bit = tmp % 8;
			filename << m_deviceSerialNumber << "-" << to_string(cur+1)<<"-" << to_string(channle) << "-" << to_string(bit) << "-" << ".png";
		}
		nums++;
		convertedImage->Save(filename.str().c_str());
		image->Release();
	};

private:
	string m_deviceSerialNumber;
	int nums = 0;
};

class CamerasProxy : public AbstractCamera
{

private:
	string _path;
	const string RGBCameraSerial = "20137880";
	const string SPECameraSerial = "16138111";
	std::shared_ptr<ImageEventHandlerImpl> SPEImpl;
	std::shared_ptr<ImageEventHandlerImpl> RGBImpl;
	enum triggerType
	{
		SOFTWARE,
		HARDWARE
	};

	const triggerType chosenTrigger = HARDWARE;
	int ConfigureTrigger(INodeMap& nodeMap)
	{
		int result = 0;

		cout << endl << endl << "*** CONFIGURING TRIGGER ***" << endl << endl;

		if (chosenTrigger == SOFTWARE)
		{
			cout << "Software trigger chosen..." << endl;
		}
		else if (chosenTrigger == HARDWARE)
		{
			cout << "Hardware trigger chosen..." << endl;
		}

		try
		{
			//
			// Ensure trigger mode off
			//
			// *** NOTES ***
			// The trigger must be disabled in order to configure whether the source
			// is software or hardware.
			//
			CEnumerationPtr ptrTriggerMode = nodeMap.GetNode("TriggerMode");
			if (!IsAvailable(ptrTriggerMode) || !IsReadable(ptrTriggerMode))
			{
				cout << "Unable to disable trigger mode (node retrieval). Aborting..." << endl;
				return -1;
			}

			CEnumEntryPtr ptrTriggerModeOff = ptrTriggerMode->GetEntryByName("Off");
			if (!IsAvailable(ptrTriggerModeOff) || !IsReadable(ptrTriggerModeOff))
			{
				cout << "Unable to disable trigger mode (enum entry retrieval). Aborting..." << endl;
				return -1;
			}

			ptrTriggerMode->SetIntValue(ptrTriggerModeOff->GetValue());

			cout << "Trigger mode disabled..." << endl;

			//
			// Set TriggerSelector to FrameStart
			//
			// *** NOTES ***
			// For this example, the trigger selector should be set to frame start.
			// This is the default for most cameras.
			//
			CEnumerationPtr ptrTriggerSelector = nodeMap.GetNode("TriggerSelector");
			if (!IsAvailable(ptrTriggerSelector) || !IsWritable(ptrTriggerSelector))
			{
				cout << "Unable to set trigger selector (node retrieval). Aborting..." << endl;
				return -1;
			}

			CEnumEntryPtr ptrTriggerSelectorFrameStart = ptrTriggerSelector->GetEntryByName("FrameStart");
			if (!IsAvailable(ptrTriggerSelectorFrameStart) || !IsReadable(ptrTriggerSelectorFrameStart))
			{
				cout << "Unable to set trigger selector (enum entry retrieval). Aborting..." << endl;
				return -1;
			}

			ptrTriggerSelector->SetIntValue(ptrTriggerSelectorFrameStart->GetValue());

			cout << "Trigger selector set to frame start..." << endl;

			//
			// Select trigger source
			//
			// *** NOTES ***
			// The trigger source must be set to hardware or software while trigger
			// mode is off.
			//
			CEnumerationPtr ptrTriggerSource = nodeMap.GetNode("TriggerSource");
			if (!IsAvailable(ptrTriggerSource) || !IsWritable(ptrTriggerSource))
			{
				cout << "Unable to set trigger mode (node retrieval). Aborting..." << endl;
				return -1;
			}

			if (chosenTrigger == SOFTWARE)
			{
				// Set trigger mode to software
				CEnumEntryPtr ptrTriggerSourceSoftware = ptrTriggerSource->GetEntryByName("Software");
				if (!IsAvailable(ptrTriggerSourceSoftware) || !IsReadable(ptrTriggerSourceSoftware))
				{
					cout << "Unable to set trigger mode (enum entry retrieval). Aborting..." << endl;
					return -1;
				}

				ptrTriggerSource->SetIntValue(ptrTriggerSourceSoftware->GetValue());

				cout << "Trigger source set to software..." << endl;
			}
			else if (chosenTrigger == HARDWARE)
			{
				// Set trigger mode to hardware ('Line0')
				CEnumEntryPtr ptrTriggerSourceHardware = ptrTriggerSource->GetEntryByName("Line0");
				if (!IsAvailable(ptrTriggerSourceHardware) || !IsReadable(ptrTriggerSourceHardware))
				{
					cout << "Unable to set trigger mode (enum entry retrieval). Aborting..." << endl;
					return -1;
				}

				ptrTriggerSource->SetIntValue(ptrTriggerSourceHardware->GetValue());

				cout << "Trigger source set to hardware..." << endl;

				// Set trigger mode to hardware()
				CEnumerationPtr ptrTriggerActivation = nodeMap.GetNode("TriggerActivation");
				if (!IsAvailable(ptrTriggerActivation) || !IsWritable(ptrTriggerActivation))
				{
					cout << "Unable to set trigger mode xxx. Aborting..." << endl;
					return -1;
				}
				CEnumEntryPtr ptrTriggerModeHardware = ptrTriggerActivation->GetEntryByName("RisingEdge");
				if (!IsAvailable(ptrTriggerModeHardware) || !IsReadable(ptrTriggerModeHardware))
				{
					cout << "Unable to set trigger mode (ttt). Aborting..." << endl;
					return -1;
				}
				ptrTriggerActivation->SetIntValue(ptrTriggerModeHardware->GetValue());

				//CEnumerationPtr ptrTriggerDelayEnabled = nodeMap.GetNode("TriggerDelayEnabled");
				//if (!IsAvailable(ptrTriggerDelayEnabled) || !IsWritable(ptrTriggerDelayEnabled))
				//{
				//	cout << "Unable to set trigger mode TriggerTriggerDelay. Aborting..." << endl;
				//	cout << IsAvailable(ptrTriggerDelayEnabled) << " " << IsWritable(ptrTriggerDelayEnabled)<<endl;
				//	return -1;
				//}
				//CEnumEntryPtr ptrTriggerTriggerDelayTrue = ptrTriggerDelayEnabled->GetEntryByName("True");
				//if (!IsAvailable(ptrTriggerTriggerDelayTrue) || !IsReadable(ptrTriggerTriggerDelayTrue))
				//{
				//	cout << "Unable to set trigger mode TriggerTriggerDelay true. Aborting..." << endl;
				//	return -1;
				//}
				//ptrTriggerDelayEnabled->SetIntValue(ptrTriggerTriggerDelayTrue->GetValue());

				//CFloatPtr ptrTriggerDelay = nodeMap.GetNode("TriggerDelay");
				//if (!IsAvailable(ptrTriggerDelay) || !IsWritable(ptrTriggerDelay))
				//{
				//	cout << "Unable to set ptrTriggerDelay time. Aborting..." << endl << endl;
				//	return -1;
				//}
				//ptrTriggerDelay->SetValue(10000);

				//cout<<"delay"<<ptrTriggerDelay->GetValue()<<endl;

			}



			//
			// Turn trigger mode on
			//
			// *** LATER ***
			// Once the appropriate trigger source has been set, turn trigger mode
			// on in order to retrieve images using the trigger.
			//

			CEnumEntryPtr ptrTriggerModeOn = ptrTriggerMode->GetEntryByName("On");
			if (!IsAvailable(ptrTriggerModeOn) || !IsReadable(ptrTriggerModeOn))
			{
				cout << "Unable to enable trigger mode (enum entry retrieval). Aborting..." << endl;
				return -1;
			}

			ptrTriggerMode->SetIntValue(ptrTriggerModeOn->GetValue());

			// TODO: Blackfly and Flea3 GEV cameras need 1 second delay after trigger mode is turned on

			cout << "Trigger mode turned back on..." << endl << endl;
			if (chosenTrigger == HARDWARE) {
				CEnumerationPtr ptrTriggerOverlap = nodeMap.GetNode("TriggerOverlap");
				if (!IsAvailable(ptrTriggerOverlap) || !IsWritable(ptrTriggerOverlap))
				{
					cout << "Unable to set trigger mode xxx. Aborting..." << endl;
					return -1;
				}
				CEnumEntryPtr ptrTriggerOverlapReadout = ptrTriggerOverlap->GetEntryByName("ReadOut");
				if (!IsAvailable(ptrTriggerOverlapReadout) || !IsReadable(ptrTriggerOverlapReadout))
				{
					cout << "Unable to set trigger mode (ttt). Aborting..." << endl;
					return -1;
				}
				ptrTriggerOverlap->SetIntValue(ptrTriggerOverlapReadout->GetValue());

			}


		}
		catch (Spinnaker::Exception& e)
		{
			cout << "Error: " << e.what() << endl;
			result = -1;
		}

		return result;
	}

public:
    CamerasProxy(){};
	//ing
	void saveimg() {
		int i = 0;
		while (!rgbqueue.empty()) {
			auto img = rgbqueue.front();
			string name = ""
			img->Save()
		}
	}
	//override 标记函数为重写，用于编译器进行检查
    virtual int cameraInit() override{
		//检查计算机文件读写
		FILE* tempFile = fopen("test.txt", "w+");
		if (tempFile == nullptr)
			return -1;
		fclose(tempFile);
		remove("test.txt");

		system = System::GetInstance();
		camList = system->GetCameras();
		numCameras = camList.GetSize();
		cout <<"find camera"<< numCameras << endl;
		SPEImpl = make_shared<ImageEventHandlerImpl>(SPECameraSerial);
		RGBImpl = make_shared<ImageEventHandlerImpl>(RGBCameraSerial);
        return 0;
    }

	virtual void setWorkPath(const char * path) {
		_path = path;
		
	}

	virtual int setRGBCameraExposure(double time_us) {
		int result = 0;
		CameraPtr pCam = camList.GetBySerial(RGBCameraSerial);
		if (pCam == nullptr) {
			return -1;
		}
		pCam->Init();
		INodeMap& nodeMap = pCam->GetNodeMap();
		try
		{
			CEnumerationPtr ptrExposureAuto = nodeMap.GetNode("ExposureAuto");
			if (!IsAvailable(ptrExposureAuto) || !IsWritable(ptrExposureAuto))
			{
				cout << "Unable to disable automatic exposure (node retrieval). Aborting..." << endl << endl;
				return -1;
			}

			CEnumEntryPtr ptrExposureAutoOff = ptrExposureAuto->GetEntryByName("Off");
			if (!IsAvailable(ptrExposureAutoOff) || !IsReadable(ptrExposureAutoOff))
			{
				cout << "Unable to disable automatic exposure (enum entry retrieval). Aborting..." << endl << endl;
				return -1;
			}

			ptrExposureAuto->SetIntValue(ptrExposureAutoOff->GetValue());

			cout << "Automatic exposure disabled..." << endl;

			CFloatPtr ptrExposureTime = nodeMap.GetNode("ExposureTime");
			if (!IsAvailable(ptrExposureTime) || !IsWritable(ptrExposureTime))
			{
				cout << "Unable to set exposure time. Aborting..." << endl << endl;
				return -1;
			}

			// Ensure desired exposure time does not exceed the maximum
			const double exposureTimeMax = ptrExposureTime->GetMax();
			double exposureTimeToSet = time_us;

			if (exposureTimeToSet > exposureTimeMax)
			{
				exposureTimeToSet = exposureTimeMax;
			}

			ptrExposureTime->SetValue(exposureTimeToSet);

			cout << std::fixed << "Exposure time set to " << exposureTimeToSet << " us..." << endl << endl;
		}
		catch (Spinnaker::Exception& e)
		{
			cout << "Error: " << e.what() << endl;
			result = -1;
		}
		return result;
	}

	virtual int setSPECameraExposure(double time_us) { 
		int result = 0;
		CameraPtr pCam = camList.GetBySerial(SPECameraSerial);
		if (pCam == nullptr) {
			return -1;
		}
		pCam->Init();
		INodeMap& nodeMap = pCam->GetNodeMap();
		try
		{
			CEnumerationPtr ptrExposureAuto = nodeMap.GetNode("ExposureAuto");
			if (!IsAvailable(ptrExposureAuto) || !IsWritable(ptrExposureAuto))
			{
				cout << "Unable to disable automatic exposure (node retrieval). Aborting..." << endl << endl;
				return -1;
			}

			CEnumEntryPtr ptrExposureAutoOff = ptrExposureAuto->GetEntryByName("Off");
			if (!IsAvailable(ptrExposureAutoOff) || !IsReadable(ptrExposureAutoOff))
			{
				cout << "Unable to disable automatic exposure (enum entry retrieval). Aborting..." << endl << endl;
				return -1;
			}

			ptrExposureAuto->SetIntValue(ptrExposureAutoOff->GetValue());

			cout << "Automatic exposure disabled..." << endl;

			CFloatPtr ptrExposureTime = nodeMap.GetNode("ExposureTime");
			if (!IsAvailable(ptrExposureTime) || !IsWritable(ptrExposureTime))
			{
				cout << "Unable to set exposure time. Aborting..." << endl << endl;
				return -1;
			}

			// Ensure desired exposure time does not exceed the maximum
			const double exposureTimeMax = ptrExposureTime->GetMax();
			double exposureTimeToSet = time_us;

			if (exposureTimeToSet > exposureTimeMax)
			{
				exposureTimeToSet = exposureTimeMax;
			}

			ptrExposureTime->SetValue(exposureTimeToSet);

			cout << std::fixed << "Exposure time set to " << exposureTimeToSet << " us..." << endl << endl;
		}
		catch (Spinnaker::Exception& e)
		{
			cout << "Error: " << e.what() << endl;
			result = -1;
		}
		return result;
	}
	virtual int runSingleAcq(){ return 0; };
	
	virtual int runTriggerRGBAcq() { 
		CameraPtr pCamRGB = camList.GetBySerial(RGBCameraSerial);
		if (pCamRGB == nullptr) {
			cout << "cann't find RGBcamera" << endl;
			return -1; 
		}
		INodeMap& RGBnodeMap = pCamRGB->GetNodeMap();
		ConfigureTrigger(RGBnodeMap);

		CEnumerationPtr RGBptrAcquisitionMode = RGBnodeMap.GetNode("AcquisitionMode");
		if (!IsWritable(RGBptrAcquisitionMode))
		{
			cout << "Unable to set acquisition mode to continuous (node retrieval). Aborting..." << endl << endl;
			return -1;
		}
		CEnumEntryPtr RGBptrAcquisitionModeContinuous = RGBptrAcquisitionMode->GetEntryByName("Continuous");
		if (!IsReadable(RGBptrAcquisitionModeContinuous))
		{
			cout << "Unable to set acquisition mode to continuous (enum entry retrieval). Aborting..." << endl << endl;
			return -1;
		}
		const int64_t RGBacquisitionModeContinuous = RGBptrAcquisitionModeContinuous->GetValue();
		RGBptrAcquisitionMode->SetIntValue(RGBacquisitionModeContinuous);

		pCamRGB->RegisterEventHandler(*RGBImpl);
		pCamRGB->BeginAcquisition();
		
		//for (size_t i = 0; i < 10; i++)
		//{
		//	if (chosenTrigger == SOFTWARE)
		//	{
		//		// Get user input
		//		cout << "Press the Enter key to initiate software trigger." << endl;
		//		getchar();

		//		// Execute software trigger
		//		CCommandPtr RGBptrSoftwareTriggerCommand = RGBnodeMap.GetNode("TriggerSoftware");
		//		if (!IsAvailable(RGBptrSoftwareTriggerCommand) || !IsWritable(RGBptrSoftwareTriggerCommand))
		//		{
		//			cout << "Unable to execute trigger. Aborting..." << endl;
		//			return -1;
		//		}

		//		RGBptrSoftwareTriggerCommand->Execute();
		//		CCommandPtr SPEptrSoftwareTriggerCommand = SPEnodeMap.GetNode("TriggerSoftware");
		//		if (!IsAvailable(SPEptrSoftwareTriggerCommand) || !IsWritable(SPEptrSoftwareTriggerCommand))
		//		{
		//			cout << "Unable to execute trigger. Aborting..." << endl;
		//			return -1;
		//		}

		//		SPEptrSoftwareTriggerCommand->Execute();

		//		// TODO: Blackfly and Flea3 GEV cameras need 2 second delay after software trigger
		//	}


		//}
		return 0;
	};

	virtual int runTriggerSPEAcq() {
		CameraPtr pCamSPE = camList.GetBySerial(SPECameraSerial);
		if (pCamSPE == nullptr) {
			cout << "cann't find SPEcamera" << endl;
			return -1;
		}
		INodeMap& SPEnodeMap = pCamSPE->GetNodeMap();
		ConfigureTrigger(SPEnodeMap);

		CEnumerationPtr SPEptrAcquisitionMode = SPEnodeMap.GetNode("AcquisitionMode");
		if (!IsWritable(SPEptrAcquisitionMode))
		{
			cout << "Unable to set acquisition mode to continuous (node retrieval). Aborting..." << endl << endl;
			return -1;
		}
		CEnumEntryPtr SPEptrAcquisitionModeContinuous = SPEptrAcquisitionMode->GetEntryByName("Continuous");
		if (!IsReadable(SPEptrAcquisitionModeContinuous))
		{
			cout << "Unable to set acquisition mode to continuous (enum entry retrieval). Aborting..." << endl << endl;
			return -1;
		}
		const int64_t SPEacquisitionModeContinuous = SPEptrAcquisitionModeContinuous->GetValue();
		SPEptrAcquisitionMode->SetIntValue(SPEacquisitionModeContinuous);

		pCamSPE->RegisterEventHandler(*SPEImpl);
		pCamSPE->BeginAcquisition();
		return 0;
	}

	virtual int stopAcq() override {
		CameraPtr pCamRGB = camList.GetBySerial(RGBCameraSerial);
		if (pCamRGB == nullptr) {
			cout << "RGB" << endl;
			return 0;
		}
		CameraPtr pCamSPE = camList.GetBySerial(SPECameraSerial);
		if (pCamRGB == nullptr) {
			cout << "spe" << endl;
			return 0;
		}
		cout << "Press the Enter key to stop acquisition" << endl;
		getchar();
		pCamRGB->EndAcquisition();
		pCamSPE->EndAcquisition();
		return 0;

	}

	virtual bool camerasReady()override {
		return (camList.GetSize() == 2);
	}

    virtual ~CamerasProxy(){
		camList.Clear();
		system->ReleaseInstance();
	}

	

	int AcquireImages()
	{
		CameraPtr pCam = camList.GetBySerial(RGBCameraSerial);
		INodeMap& nodeMapTLDevice = pCam->GetTLDeviceNodeMap();
		// Retrieve GenICam nodemap
		INodeMap& nodeMap = pCam->GetNodeMap();
		int result = 0;

		cout << endl << "*** IMAGE ACQUISITION ***" << endl << endl;

		try
		{
			// Set acquisition mode to continuous
			CEnumerationPtr ptrAcquisitionMode = nodeMap.GetNode("AcquisitionMode");
			if (!IsAvailable(ptrAcquisitionMode) || !IsWritable(ptrAcquisitionMode))
			{
				cout << "Unable to set acquisition mode to continuous (node retrieval). Aborting..." << endl << endl;
				return -1;
			}

			CEnumEntryPtr ptrAcquisitionModeContinuous = ptrAcquisitionMode->GetEntryByName("Continuous");
			if (!IsAvailable(ptrAcquisitionModeContinuous) || !IsReadable(ptrAcquisitionModeContinuous))
			{
				cout << "Unable to set acquisition mode to continuous (entry 'continuous' retrieval). Aborting..." << endl
					<< endl;
				return -1;
			}

			int64_t acquisitionModeContinuous = ptrAcquisitionModeContinuous->GetValue();

			ptrAcquisitionMode->SetIntValue(acquisitionModeContinuous);

			cout << "Acquisition mode set to continuous..." << endl;

			// Begin acquiring images
			pCam->BeginAcquisition();

			cout << "Acquiring images..." << endl;

			// Retrieve device serial number for filename
			gcstring deviceSerialNumber("");

			CStringPtr ptrStringSerial = nodeMapTLDevice.GetNode("DeviceSerialNumber");
			if (IsAvailable(ptrStringSerial) && IsReadable(ptrStringSerial))
			{
				deviceSerialNumber = ptrStringSerial->GetValue();

				cout << "Device serial number retrieved as " << deviceSerialNumber << "..." << endl;
			}
			cout << endl;

			// Get the value of exposure time to set an appropriate timeout for GetNextImage
			CFloatPtr ptrExposureTime = nodeMap.GetNode("ExposureTime");
			if (!IsAvailable(ptrExposureTime) || !IsReadable(ptrExposureTime))
			{
				cout << "Unable to read exposure time. Aborting..." << endl << endl;
				return -1;
			}
			// The exposure time is retrieved in µs so it needs to be converted to ms to keep consistency with the unit
			// being used in GetNextImage
			double timeout = ptrExposureTime->GetValue() / 1000 + 1000;

			// Retrieve, convert, and save images
			const unsigned int k_numImages = 5;

			for (unsigned int imageCnt = 0; imageCnt < k_numImages; imageCnt++)
			{
				try
				{
					// Retrieve next received image and ensure image completion
					// By default, GetNextImage will block indefinitely until an image arrives.
					// In this example, the timeout value is set to [exposure time + 1000]ms to ensure that an image has
					// enough time to arrive under normal conditions
					ImagePtr pResultImage = pCam->GetNextImage(timeout);

					if (pResultImage->IsIncomplete())
					{
						cout << "Image incomplete with image status " << pResultImage->GetImageStatus() << "..." << endl
							<< endl;
					}
					else
					{
						// Print image information
						cout << "Grabbed image " << imageCnt << ", width = " << pResultImage->GetWidth()
							<< ", height = " << pResultImage->GetHeight() << endl;

						// Convert image to mono 8
						ImagePtr convertedImage = pResultImage->Convert(PixelFormat_Mono8, HQ_LINEAR);

						// Create a unique filename
						ostringstream filename;

						filename << "Exposure-";
						if (deviceSerialNumber != "")
						{
							filename << deviceSerialNumber.c_str() << "-";
						}
						filename << imageCnt << ".jpg";

						// Save image
						convertedImage->Save(filename.str().c_str());

						cout << "Image saved at " << filename.str() << endl;
					}

					// Release image
					pResultImage->Release();

					cout << endl;
				}
				catch (Spinnaker::Exception& e)
				{
					cout << "Error: " << e.what() << endl;
					result = -1;
				}
			}

			// End acquisition
			pCam->EndAcquisition();
		}
		catch (Spinnaker::Exception& e)
		{
			cout << "Error: " << e.what() << endl;
			result = -1;
		}

		return result;
	}
private:
	SystemPtr system;
	CameraList camList;
	unsigned int numCameras = -1;
};


#endif